import { Container } from '../Container';
import { EventHandler } from '../Services/Common';
import { BaseGui } from "../Services/BaseGui";
import { DataService } from "../Services/DataService";
import { ViewerService } from "../Services/ViewerService";
import { GroupManService, GroupEntity } from "../Services/GroupManService";
export class GroupManGui extends BaseGui {
    title = `模型集合[${GroupManGui.name}]`;
    private ViewerService = Container.defineProperty(ViewerService);
    private DataService = Container.defineProperty(DataService);
    private GroupManService = Container.defineProperty(GroupManService);
    Current?: GroupEntity;
    CurrentChanged = new EventHandler();
    private getData() {
        return this.GroupManService.getData();
    }

    protected async UpdateGui() {
        await this.ViewerService.ViewerPromise;
        let array = this.getData().groups;
        let gui = this.gui;

        var fun = {
            enableThemeColor:false,
            add: () => {
                let name = "group" + (array.length + 1);
                let selection = this.getViewSelect();
                if (selection.length == 0) {
                    alert("no model selected");
                    return;
                }
                const newData: GroupEntity = { name, selection, disableColor: false, color: [255, 0, 0] };
                array.push(newData);
                fun.data = array.length - 1;
                this.updateViewer();
                updateGUI();
                fun._dataChange();
            },
            remove: () => {
                if (!window.confirm("确实要清除选中项吗？"))
                    return;
                array.splice(fun.data, 1);
                fun.data = fun.data < array.length ? fun.data : fun.data - 1;
                this.updateViewer();
                updateGUI();
                fun._dataChange();
            },
            clear: () => {
                if (array.length == 0)
                    return;
                if (!window.confirm("确实要清除所有？"))
                    return;
                array.splice(0, array.length);
                fun.data = -1;
                this.updateViewer();
                updateGUI();
                fun._dataChange();
            },
            data: -1,
            _dataOptions: () => {
                let options = {} as any;
                array.forEach((v: { name: any; }, i: number) => {
                    const name = `[${i + 1}]${v.name}`;
                    options[name] = i;
                })
                return options;
            },
            _dataChange: () => {
                let data = array[fun.data];
                fun.dataName = data.name;
                fun.disableColor = data.disableColor == undefined ? false : data.disableColor;
                fun.color = data.color;
                //fun.hide = data.hide == undefined ? false : data.hide;
                //fun.transparent = data.transparent == undefined ? false : data.transparent;
                updateGUI();
                this.Current = data;
                this.CurrentChanged.dispatchEvent();
            },
            _enableThemeColorChanged:()=>{
                this.GroupManService.enableThemeColor = fun.enableThemeColor;
                this.updateViewer();
            },
            dataName: "",
            color: [255, 255, 255],
            disableColor: false,
            updateSelection: () => {
                let selection = this.getViewSelect();
                if (selection.length == 0) {
                    alert("no model selected");
                    return;
                }
                if (!window.confirm("确实要更新集合？"))
                    return;
                let data = array[fun.data];
                data.selection = selection;
                this.updateViewer();
                fun._dataChange();
                alert("Done");
            },
            selectToViewer: () => {
                let data = array[fun.data];
                this.setViewSelect(data);
            },
            addSelectionToViewer: () => {
                let data = array[fun.data];
                this.addViewSelect(data);
            },
            removeSelectionToViewer: () => {
                let data = array[fun.data];
                this.delViewSelect(data);
            },
            findByViewer: () => {
                this.ViewerService.viewer.getAggregateSelection((model, dbId) => {
                    const name = this.GroupManService.getModelName(model);
                    let findItems = this.getData().groups.map((item, index) => ({ item, index }))
                        .filter(_ => _.item.selection.findIndex(v => v.name == name && v.dbIds.indexOf(dbId) >= 0) >= 0);

                    if (findItems.length == 0)
                        window.alert("未找到");
                    else {
                        let message = findItems.map(_ => `[${_.index + 1}]${_.item.name}`).join("/n/t");
                        window.alert(message);
                    }
                    if (findItems.length == 1) {
                        fun.data = findItems[0].index;
                        fun._dataChange();
                    }
                })
            },
            autoFindByViewer: false,
            _dataNameChanged: () => {
                array[fun.data].name = fun.dataName;
                updateGUI();
                this.updateViewer();
            },
            _disableColorChange: () => {
                array[fun.data].disableColor = fun.disableColor;
                this.updateViewer();
            },
            _colorChange: () => {
                array[fun.data].color = fun.color;
                this.updateViewer();
            },
        }
        this.onViewerSelectChanged = () => {
            if (fun.autoFindByViewer)
                fun.findByViewer();
        }

        let updateGUI = () => {
            Array.from(gui.__controllers).forEach(_ => {
                gui.remove(_);
            });
            this.guiService.buildGUIByObject(gui, fun);
        }
        updateGUI();
        return this;
    }
    async Init() {
        this.DataService.OnDataChanged.addEventListener(() => {
            this.Refresh();
        });
        await this.ViewerService.ViewerPromise;
        this.ViewerService.viewer.addEventListener(Sippreep.Viewing.AGGREGATE_SELECTION_CHANGED_EVENT, () => {
            this.onViewerSelectChanged();
        });
    }

    private updateViewer() {
        this.GroupManService.updateViewer();
    }
    private getViewSelect() {
        // 用户选择 -> 转成叶子节点重新选择
        // this.ViewerService.viewer.getAllModels().map(m => {
        //     const flatten = this.nodeFlatten(m, m.selector.getSelection());
        //     m.selector.setSelection(flatten, Sippreep.Viewing.SelectionMode.LEAF_OBJECT as any);
        // });
        return this.ViewerService.viewer.getAllModels().map(m => {
            return {
                name: this.GroupManService.getModelName(m),
                dbIds: m.selector.getSelection(),
                // dbIds: this.nodeFlatten(m, m.selector.getSelection()),
            }
        }).filter(_ => _.dbIds.length > 0);
    }
    /**
     * 获取叶子节点
     *
     * @param m
     * @param dbids 传入dbid数组
     * @returns 返回所代表的叶子节点
     */
    private nodeFlatten(m: Sippreep.Viewing.Model, dbids: number[]): number[] {
        const flattenSet = new Set<number>();
        const tree = m.getInstanceTree();
        dbids.forEach(dbid => {
            tree.enumNodeChildren(dbid, d => {
                if (tree.getChildCount(d) == 0) {
                    flattenSet.add(d);
                }
            }, true);
        });
        return Array.from(flattenSet);
    }

    private setViewSelect(data: GroupEntity) {
        this.ViewerService.viewer.getAllModels().forEach(m => {
            m.selector.clearSelection();
            let item = data.selection.find((s) => s.name == this.GroupManService.getModelName(m));
            if (item)
                m.selector.setSelection(item.dbIds, 0);
        });
    }
    private addViewSelect(data: GroupEntity) {
        this.ViewerService.viewer.getAllModels().forEach(m => {
            let item = data.selection.find((s) => s.name == this.GroupManService.getModelName(m));
            if (item) {
                let dbids = m.selector.getSelection().concat(...item.dbIds);
                m.selector.setSelection(dbids, 0);
            }
        });
    }
    private delViewSelect(data: GroupEntity) {
        this.ViewerService.viewer.getAllModels().forEach(m => {
            let item = data.selection.find((s) => s.name == this.GroupManService.getModelName(m));
            if (item)
                item.dbIds.map(_ => {
                    m.selector.clearNodeSelection(_);
                });
        });
    }
    private onViewerSelectChanged = () => {

    }
}
export class GroupTransformGui extends BaseGui {
    title = `模型变换[${GroupTransformGui.name}]`;
    ViewerService = Container.defineProperty(ViewerService);
    GroupManService = Container.defineProperty(GroupManService);
    GroupManGui = Container.defineProperty(GroupManGui);
    Init(): void {
        this.GroupManGui.CurrentChanged.addEventListener(() => {
            this.Refresh();
        })
    }
    async UpdateGui() {
        let group = this.GroupManGui.Current as GroupEntity;
        if (!group)
            return;
        let fun = {
            parent: "",
            center: "[0,0,0]",
            position: "[0,0,0]",
            rotation: "[0,0,0]",
            axisAngle: "[0,0,0,0]",
            scale: "[1,1,1]",
            ComputingBoxCenter: () => {
                let data = group;
                let box = this.GroupManService.getBox(data);
                if (!data.transform) {
                    data.transform = this.GroupManService.transformDefault()
                }
                data.transform!.center = box.center().toArray();
                this.GroupManService.updateViewer();
                this.Refresh();
            },
            ShowCenterTransformControl: async () => {
                let data = group;
                let object3D = this.GroupManService.getWorldTransformByGroup(data);

                let plugin = await this.ViewerService.getTransformControls();
                plugin.setObject(object3D.parent);
                plugin.onObjectChange = () => {
                    this.GroupManService.setWorldTransformByGroup(data, object3D);
                    this.GroupManService.updateViewer();
                    this.Refresh();
                }

            },
            ShowTransformControl: async () => {
                let data = group;
                let object3D = this.GroupManService.getWorldTransformByGroup(data);

                let plugin = await this.ViewerService.getTransformControls();
                plugin.setObject(object3D);
                plugin.onObjectChange = () => {
                    this.GroupManService.setWorldTransformByGroup(data, object3D);
                    this.GroupManService.updateViewer();
                    this.Refresh();
                }
            },
            CloseTransformControl: async () => {
                let plugin = await this.ViewerService.getTransformControls();
                plugin.setObject(null!);
            },
            _parentChanged: () => {
                fun._tranformChanged();
            },
            _tranformChanged: () => {
                group.transformParent = fun.parent;
                group.transform = {
                    center: JSON.parse(fun.center),
                    position: JSON.parse(fun.position),
                    rotation: JSON.parse(fun.rotation),
                    axisAngle: JSON.parse(fun.axisAngle),
                    scale: JSON.parse(fun.scale),
                };
                this.GroupManService.updateViewer();
            },
            _centerChanged: () => {
                fun._tranformChanged();
            },
            _positionChanged: () => {
                fun._tranformChanged();
            },
            _rotationChanged: () => {
                fun._tranformChanged();
            },
            _axisAngleChanged: () => {
                fun._tranformChanged();
            },
            _scaleChanged: () => {
                fun._tranformChanged();
            },
        }

        let data = group;
        fun.parent = data.transformParent || "";
        let tranform = data.transform || this.GroupManService.transformDefault()
        fun.center = JSON.stringify(tranform.center);
        fun.position = JSON.stringify(tranform.position);
        fun.rotation = JSON.stringify(tranform.rotation);
        fun.axisAngle = JSON.stringify(tranform.axisAngle || [0, 0, 0, 0]);
        fun.scale = JSON.stringify(tranform.scale);

        let gui = this.gui;
        let updateGUI = () => {
            Array.from(gui.__controllers).forEach(_ => {
                gui.remove(_);
            });
            this.guiService.buildGUIByObject(gui, fun);
        }
        updateGUI();
        return this;
    }
}

export class Transform {
    ViewerService = Container.defineProperty(ViewerService);
    set Data(v: {
        center: number[]
        position: number[]
        rotation: number[]
        scale: number[]
    }) {
        this.position.fromArray(v.center);
        this.updateViewer();
    }
    private Size = 0.01
    private overlayName = "tranform";
    private mesh: THREE.Object3D = null!;
    private position: THREE.Vector3 = null!;
    Show() {
        let viewer = this.ViewerService.viewer as Sippreep.Viewing.GuiViewer3D;
        viewer.overlays.addScene(this.overlayName);
        viewer.impl.addOverlay(this.overlayName, this.mesh);

        const box = new THREE.Box3();
        box.expandByPoint(this.position);
        viewer.getAllModels().forEach(_ => {
            let temp = _.getBoundingBox();
            box.expandByPoint(temp.min);
            box.expandByPoint(temp.max);
        });
        viewer.navigation.fitBounds(false, box);
    }
    Close() {
        let viewer = this.ViewerService.viewer as Sippreep.Viewing.GuiViewer3D;
        viewer.overlays.clearScene(this.overlayName);
    }
    async Init() {
        let viewer = await this.ViewerService.ViewerPromise;
        this.position = new THREE.Vector3();

        const mat = new THREE.MeshBasicMaterial();
        mat.color = new THREE.Color(1, 0, 0);
        mat.depthTest = false;
        mat.depthWrite = false;
        const geo = new THREE.SphereGeometry(1, 12, 12);
        const sphere = new THREE.Mesh(geo, mat);
        sphere.position.copy(this.position);
        this.mesh = sphere;

        viewer.addEventListener(Sippreep.Viewing.CAMERA_CHANGE_EVENT, () => {
            this.updateViewer();
        });
    }
    private updateViewer() {
        let viewer = this.ViewerService.viewer;
        if (this.mesh) {
            let dis = viewer.getCamera().position.distanceTo(this.position);
            dis = dis * this.Size;
            this.mesh.scale.set(dis, dis, dis);
            this.mesh.position.copy(this.position);
            viewer.impl.invalidate(false, false, true);
        }
    }
}